#ifndef XG_HTTPSERVER_H
#define XG_HTTPSERVER_H
////////////////////////////////////////////////////////
#include <unordered_map>
#include "HttpResponse.h"
#include "../stdx/File.h"
#include "../clib/netx.h"

#ifndef HTTP_SERVER_IPC_SHMSIZE
#define HTTP_SERVER_IPC_SHMSIZE		4096
#endif

#ifndef HTTP_RESPONSE_BATCHSIZE
#define HTTP_RESPONSE_BATCHSIZE		8 * 1024	
#endif

class HttpStream;
class SSLWorkItem;

class CheckHost
{
	class Counter
	{
	public:
		size_t num = 0;
		time_t ctime = 0;
	};

	unordered_map<int, Counter> hostmap;

public:
	int check(int host, int maxcnt);
};

class HttpRequestItem : public WorkItem
{
	friend class HttpServer;

private:
	int maxsz;
	int readed;
	string addr;
	time_t utime;
	sp<Socket> sock;
	SmartBuffer buffer;
	HttpRequest request;

public:
	void run();
	int process();
	bool runnable();

	HttpRequestItem(sp<Socket> sock, const string& addr)
	{
		this->buffer.malloc(HTTP_REQHDR_MAXSIZE);
		this->utime = time(NULL);
		this->sock = sock;
		this->addr = addr;
		this->readed = 0;
		this->maxsz = 0;
	}
};

class HttpResponseItem : public WorkItem
{
	friend class HttpRequestItem;

private:
	int writed;
	int filelen;
	string addr;
	time_t utime;
	sp<XFile> file;
	sp<Socket> sock;
	SmartBuffer data;

public:
	void run();
	bool runnable();
	int init(SmartBuffer data);
	int init(HttpResponse* response, const void* msg, int len);

	bool empty() const
	{
		return writed >= data.size();
	}
	HttpResponseItem(sp<Socket> sock, const string& addr)
	{
		this->writed = 0;
		this->sock = sock;
		this->addr = addr;
		this->utime = time(NULL);
		this->filelen = XG_ERROR;
	}
};

class HttpServer : public IHttpServer
{
	friend class HttpFrame;
	friend class HttpResponse;
	friend class HttpApplication;
	friend class HttpRequestItem;
	friend class SessionCheckItem;

public:
	struct ShareData
	{
		int port;
		char host[32];
		char data[32];
		char name[MAX_PATH];
		char path[MAX_PATH];
		char logdata[8 * 1024 * 1024];
	};

	struct TransCountItem
	{
		int daily;
		int mincost;
		int maxcost;
		int meancost;
		int realtime;

		TransCountItem() : daily(0), realtime(0), mincost(24 * 3600 * 1000), maxcost(0), meancost(0){}
	};

protected:
	int port;
	int sslport;
	string host;
	string sslhost;
	CgiMapData cgidata;

	int memsz;
	int monitor;
	string name;
	string path;
	int requestimes;
	int routeswitch;
	string trustorigin;
	string sequencehead;
	u_short socktimeslist[0xFFFF];

	mutable SpinMutex mtx;
	mutable SpinMutex svrmtx;
	mutable SpinMutex hostmtx;
	mutable SpinMutex transmtx;
	mutable SpinMutex streamtx;

	MemQueue mq;
	Sharemem shm;
	Semaphore sem;
	CacheFile cachefile;
	DBConnectPool* dbconnpool;

	DllFile dbconnpooldll;
	map<string, string> cfgmap;
	map<string, string> pathmap;
	map<string, string> mimemap;
	map<string, CheckHost> hostmap;
	map<string, TransCountItem> transmap;

	TSMap<string, int> accessmap;
	TSMap<string, string> cgiextmap;
	TSMap<string, CgiMapData> cgimap;
	TSMap<SOCKET, sp<HttpStream>> streamap;
	TSMap<string, sp<HttpSession>> sessmap;
	TSMap<SOCKET, sp<SSLWorkItem>> sslconnmap;
	TSMap<string, tuple<string, string, string>> cgidocmap;

	HttpServer(const HttpServer& obj);
	HttpServer() : port(0), sslport(0), memsz(-1), monitor(1), dbconnpool(NULL), routeswitch(0), requestimes(HTTP_REQUEST_MAXTIMES){}

	void close();
	void setLogCallback();
	bool loadSystem(bool updated);
	bool loadConfig(const string& filepath);

public:
	int getMemSize()
	{
		if (memsz < 0) getShareData();

		return memsz;
	}
	int getRouteSwitch() const
	{
		return routeswitch;
	}
	int getMaxRequestTimes() const
	{
		return requestimes;
	}
	map<string, TransCountItem> getTransCountMap() const
	{
		SpinLocker lk(transmtx);
		return transmap;
	}
	TransCountItem getTransCountItem(const string& url) const
	{
		string key = url;
		SpinLocker lk(transmtx);
		auto it = transmap.find(stdx::tolower(key));
		return it == transmap.end() ? TransCountItem() : it->second;
	}

	void loop();
	int getPort();
	string getName();
	string getHost();
	string getPath();
	string getSequence();
	int command(char* cmd);
	ShareData* getShareData();
	bool initSystem(bool destroyed);
	bool init(const string& filepath);
	int updateCgiData(const string& url);
	void removeSession(const string& name);
	void removeCgiAccess(const string& url);
	int getCgiAccess(const string& url) const;
	bool initDatabase(const string& filepath);
	CgiMapData getCgiMapData(const string& url);
	string getCgiExtdata(const string& url) const;
	int getCgiMap(map<string, CgiMapData>& cgimap);
	void setCgiAccess(const string& url, int access);
	bool addCgiData(string& url, const string& path = "");
	sp<Session> getSession(const string& name, int timeout = 0);
	void setCgiExtdata(const string& url, const string& extdata);
	bool updateModuleFile(const string& src, const string& dest);
	int getFileContent(const string& path, SmartBuffer& content);
	int getFileContent(const string& path, SmartBuffer& content, time_t& utime);
	sp<HttpResponse> getLocaleResult(const HttpRequest& request, int timeout = SOCKET_CONNECT_TIMEOUT);

	sp<DBConnect> getDBConnect();
	void disableDBConnect(sp<DBConnect> conn);
	string getMimeType(const string& key) const;
	map<string, tuple<string, string, string>> getCgiDocMap() const;
	tuple<string, string, string> getCgiDoc(const string& url) const;
	void setCgiDoc(const string& url, const string& reqdoc, const string& rspdoc, const string& remark);

	static void Lock();
	static void Unlock();
	static int ReleaseConnect(SOCKET conn, const char* addr);
	static int ProcessRequest(SOCKET conn, stConnectData* data);
	static int ProcessConnectClosed(SOCKET conn, stConnectData* data);
	static int ProcessConnect(SOCKET conn, stConnectData* data, const char* host, int port);
	
	static bool IsDynamicLoad();
	static HttpServer* Instance();
	static const char* GetSharememName();
	static const char* GetSemaphoreName();
	static string GetWebAppPath(const char* path, const char* filename);
};
////////////////////////////////////////////////////////
#endif
